import json
import re

import jsonpath
import requests

from apirunner.logger import setup_logger
from apirunner.sessions import Session
from apirunner.template import Template


def execute(case_info: dict, **kwargs):
    """
    接口测试用例的核心执行器
    :param case_info: 测试用例信息，是一个字典对象
    :param kwargs: 需要更新到字典对象中 key 值为 context 的字典信息
    :return: None
    """
    logger = setup_logger()
    logger.info('执行器开始启动运行->处理本条自动化用例')

    logger.info(f'用例信息：{case_info}')
    logger.info(f'用例描述：{case_info.get("desc", None)}')
    context = case_info.get('context', {})
    logger.info(f'上下文初始值：{context}')

    logger.info(f'判断上下文是否更新：{bool(kwargs)}')
    if kwargs:
        logger.info(f'上下文进行更新，更新值为：{kwargs}')
        context.update(**kwargs)
        logger.info(f'上下文更新后的值：{context}')

    steps = case_info['steps']
    for step, step_num in zip(steps, [*range(1, len(steps) + 1)]):
        request_ = step['request']
        logger.info(f'步骤{step_num}接口信息初始值：{request_}')
        old_request = request_

        if context:
            request_ = json.loads(Template(json.dumps(request_)).substitute(context)
                                  .replace('"{', '{').replace('}"', '}').replace("'", '"'))
        new_request = request_

        logger.info(f'判断步骤{step_num}接口信息是否进行上下文的变量赋值：{old_request != new_request}')
        if old_request != new_request:
            logger.info(f'步骤{step_num}接口信息进行上下文的变量赋值，接口信息赋值后的值：{request_}')

        logger.info(f'开始构造并发送步骤{step_num}接口')
        response = requests.request(**request_)
        logger.info(f'步骤{step_num}接口响应值：{response}')

        logger.info(f'判断步骤{step_num}是否断言：{bool(step.get("assert", []))}')
        for assert_ in step.get('assert', []):
            assert_target = assert_['target']
            assert_type = assert_['type']
            assert_value = assert_['value']
            logger.info(f'断言目标：{assert_target}')
            logger.info(f'断言类型：{assert_type}')
            logger.info(f'断言预期值：{assert_value}')

            if assert_target == '状态码':
                assert_target_value = response.status_code
            elif assert_target.startswith('$.'):
                assert_target_value = jsonpath.jsonpath(response.json(), assert_target)
            elif assert_target == 'text':
                assert_target_value = response.text
            else:
                assert_target_value = re.search(re.compile(assert_target), response.text).group()

            if type(assert_target_value) == list:
                assert_target_value = assert_target_value[0]
            logger.info(f'断言目标值：{assert_target_value}')

            if assert_type == '存在':
                # 存在指的是 jsonpath 路径
                assert_result = bool(assert_target_value) is True
            elif assert_type == '相等':
                assert_result = assert_target_value == assert_value
            elif assert_type == '包含':
                if type(assert_target_value) == list:
                    assert_result = assert_value in assert_target_value[0]
                else:
                    assert_result = assert_value in assert_target_value
            else:
                assert_result = None
            logger.info(f'断言结果：{assert_result}')

            if assert_.get("error_msg", None):
                if assert_['error_msg'].startswith('$.'):
                    assert_error_msg = jsonpath.jsonpath(response.json(), assert_['error_msg'])[0]
                else:
                    assert_error_msg = assert_["error_msg"]
            else:
                assert_error_msg = None
            assert assert_result, logger.error(f'断言失败消息: {assert_error_msg}')

        logger.info(f'判断步骤{step_num}是否提取：{bool(step.get("extract", []))}')
        for extract in step.get('extract', []):
            extract_target = extract['target']
            extract_var_name = extract['var_name']
            logger.info(f'提取目标：{extract_target}')
            logger.info(f'保存提取值变量：{extract_var_name}')
            if extract_target == 'cookies':
                extract_target_value = dict(response.cookies)
            elif extract_target.startswith('$.'):
                extract_target_value = jsonpath.jsonpath(response.json(), extract_target)[0]
            else:
                extract_target_value = None
            logger.info(f'提取目标值：{extract_target_value}')

            update_value = {extract_var_name: extract_target_value}
            context.update(update_value)
            logger.info(f'上下文进行更新，更新值：{update_value}')
            logger.info(f'上下文更新后的值：{context}')

            if extract_var_name == 'cookies':
                atte_value = {extract_var_name: extract_target_value}
                setattr(Session, 'cookies', atte_value)
                logger.info(f'设置 Session 类属性名：cookies，属性值：{atte_value}')
            elif extract_var_name == 'token':
                atte_value = {extract_var_name: extract_target_value}
                setattr(Session, 'token', atte_value)
                logger.info(f'设置 Session 类属性名：token，属性值：{atte_value}')

    logger.info('执行器运行结束->用例处理成功')
